package simulatorUS;

import javax.comm.*;
import javax.swing.Timer;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.TooManyListenersException;

@SuppressWarnings("restriction")
public class UART_Manager implements Runnable, SerialPortEventListener,
		SystemCommandExecutor, SystemEventGenerator {
	private final int DEFAULT_READ_BUF_SIZE = 1000;
	private SystemEventHandler SystemEventHandler;
	private SystemEvent sysEvent;
	private InputStream iS;
	private OutputStream oS;
	private String portName;
	private CommPortIdentifier portId;
	SerialPort serialPort;
	private final int DEFAUT_BAUD_RATE = 9600;
	private final int DEFAULT_RESPONSE_TIMEOUT = 1000;
	private static Enumeration<CommPortIdentifier> portList;
	private int numBytes;
	private boolean deviceFound = false;
	private int readBufSize;
	private byte[] readBuffer = new byte[readBufSize];
	private SystemCommand lastCommand = null;
	private int responceTimeout = DEFAULT_RESPONSE_TIMEOUT;
	private Thread th;
	public void setResponceTimeout(int responceTimeout) {
		this.responceTimeout = responceTimeout;
		timer.setDelay(this.responceTimeout);
	}

	private Timer timer;

	@SuppressWarnings({ "unchecked" })
	public UART_Manager() {
		readBufSize = DEFAULT_READ_BUF_SIZE;
		timer = new Timer(responceTimeout, new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				timer.stop();
				if (deviceFound) {
					sysEvent = new SystemEvent(this,
							UsartEventID.RESPONSE_TIMEOUT.ordinal(),
							new String(readBuffer).substring(0, numBytes));
					generateEvent(sysEvent);
				}
			}
		});
		Thread th = new Thread(this);
		th.setDaemon(true);
		th.start();
	}

	public void setResponseTimeout(int timeout) {

	}

	public String findDevicePort(String messageToDevice,
			String expectedResponse, int timeout) {
		portList = CommPortIdentifier.getPortIdentifiers();
		readBuffer = new byte[20];
		numBytes=0;
		while (portList.hasMoreElements()) {
			portId = portList.nextElement();
			if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
				try {
					if (portName != null) {
						oS.flush();
						oS.close();
						iS.close();
						serialPort.close();
						portName = null;

					}
					portName = portId.getName();
					int comNum = Integer.parseInt(portName.substring(3));
					if (/*(comNum <= 3) &&*/ (!portId.isCurrentlyOwned())) {

						serialPort = (SerialPort) portId.open("UART_Manager",
								200);

						oS = serialPort.getOutputStream();
						iS = serialPort.getInputStream();
						serialPort.setSerialPortParams(DEFAUT_BAUD_RATE,
								SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
								SerialPort.PARITY_NONE);
						// serialPort.setDTR(false);
						// serialPort.setRTS(false);
						serialPort.addEventListener(this);
						serialPort.notifyOnDataAvailable(true);
						 serialPort.enableReceiveTimeout(500);
						serialPort
								.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
						oS.write(messageToDevice.getBytes());
						Thread.sleep(timeout);
						if (readBuffer != null) {
							String str = new String(readBuffer);
							str = str.substring(0, numBytes);
							if (str.startsWith(expectedResponse)) {
								deviceFound = true;
								break;
							}
						}
						oS.flush();
						oS.close();
						iS.close();
						serialPort.close();
						deviceFound = false;
						portName = null;
						numBytes=0;
					}
				}

				catch (PortInUseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (UnsupportedCommOperationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (TooManyListenersException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
			portName = null;
		}
		return portName;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (this) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			if (lastCommand != null) {
				UsartCommandID commTitle = UsartCommandID.valueOf(lastCommand
						.getID());
				String commParams[] = lastCommand.getParams();
				switch (commTitle) {
				case FIND_DEVICE:
					String resp = findDevicePort(commParams[0], commParams[1],
							Integer.valueOf(commParams[2]));
					if (resp != null) {
						//
						sysEvent = new SystemEvent(this,
								UsartEventID.DEVICE_FOUND.ordinal(), portName);

					} else {
						sysEvent = new SystemEvent(this,
								UsartEventID.DEVICE_NOT_FOUND.ordinal());
					}
					generateEvent(sysEvent);
					break;
				case SEND_MASSAGE:
					sendMessage(lastCommand.getParams()[0]);
					timer.restart();
					break;
				}
				lastCommand = null;
			}
		}

	}
	public void sendMessage(String message) {
		try {
			oS.write(message.getBytes());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void serialEvent(SerialPortEvent event) {
		switch (event.getEventType()) {
		case SerialPortEvent.BI:
		case SerialPortEvent.OE:
		case SerialPortEvent.FE:
		case SerialPortEvent.PE:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.RI:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			break;
		case SerialPortEvent.DATA_AVAILABLE:
			readBuffer = new byte[readBufSize];
			int size = serialPort.getInputBufferSize();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			try {
				while (iS.available() > 0) {
					size = iS.available();
					numBytes = iS.read(readBuffer);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (deviceFound) {
				timer.stop();
				sysEvent = new SystemEvent(this,
						UsartEventID.MESSAGE_RECEIVED.ordinal(), new String(
								readBuffer).substring(0, numBytes));
				generateEvent(sysEvent);
			}
			break;

		}
	}

	@Override
	public void generateEvent(SystemEvent event) {
		if (SystemEventHandler != null) {
			SystemEventHandler.eventOccured(event);
		}

	}

	@Override
	public void SetSystemEventHandler(SystemEventHandler eventHandler) {
		SystemEventHandler = eventHandler;
	}

	@Override
	public void execute(SystemCommand command) {
		lastCommand = command;
		synchronized (this) {
			this.notify();
		}

	}

}
